#include <stddef.h>
//#include "../../utility/utility/timehelp.h"
#include "timermanager.h"

#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) 
#define INDEX(N) ((m_dwLastTick >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK)

#ifdef WIN32
#pragma comment(lib, "winmm.lib")
#else
#endif

CTimerManager::CTimerManager(void)
{
	m_dwLastTick = 0;
	m_poTimerResponse = NULL;
}

CTimerManager::~CTimerManager(void)
{
}

bool CTimerManager::Init( ITimerResponse* poTimerResponse, bool bExact /*= false*/ )
{
	if (NULL == poTimerResponse)
	{
		return false;
	}
    else
        m_poTimerResponse = poTimerResponse;

    m_pool_timer_info.Init(1);

	for (INT32 i = 0; i < TVN_SIZE; i++)
	{
		INIT_LIST_HEAD(m_tv5.vec+i);
		INIT_LIST_HEAD(m_tv4.vec+i);
		INIT_LIST_HEAD(m_tv3.vec+i);
		INIT_LIST_HEAD(m_tv2.vec+i);
	}

	for (INT32 i = 0; i < TVR_SIZE; i++)
	{
		INIT_LIST_HEAD(m_tv1.vec + i);
	}
    m_is_exact = bExact;
	m_dwLastTick = timeGetTime();
    return true;
}

void CTimerManager::UnInit( void )
{
    m_pool_timer_info.Destroy();
}

//HTIMER CTimerManager::AddTimer( INT32 iElapse, INT32 iCount, void* pData/*=NULL*/ )
HTIMER CTimerManager::AddTimer( INT32 iElapse, INT32 iCount, void* pData/*=NULL*/)
{
	if (iElapse <= 0)
	{
		return NULL;
	}

    STimerInfo* pstTimerInfo = m_pool_timer_info.AllocObj();
    if (NULL == pstTimerInfo)
    {
        return NULL;
    }

	pstTimerInfo->dwExpires = m_dwLastTick+iElapse;
	pstTimerInfo->iElapse	= iElapse;
    pstTimerInfo->iCount    = iCount;
    pstTimerInfo->pData     = pData;

    __AddTimer(*pstTimerInfo);

    return pstTimerInfo;
}

bool CTimerManager::ModTimer( HTIMER hTimer, INT32 iElapse, INT32 iCount, void* pData/*=NULL*/)
{
    STimerInfo* pstTimerInfo = __Handle2Info(hTimer);

    if (!pstTimerInfo)
    {
        return false;
    }

    if (pstTimerInfo->dwExpires == m_dwLastTick+iElapse && __TimerPending(*pstTimerInfo))
        return true;

    if (__TimerPending(*pstTimerInfo))
    {
        __DetachTimer(*pstTimerInfo, true);

        pstTimerInfo->dwExpires = m_dwLastTick + iElapse;
        pstTimerInfo->iElapse = iElapse;
        pstTimerInfo->iCount = iCount;

        if (pData != NULL)
        {
            pstTimerInfo->pData = pData;
        }

        __AddTimer(*pstTimerInfo);
    }
    else
    {
        pstTimerInfo->dwExpires = m_dwLastTick + iElapse;
        pstTimerInfo->iElapse = iElapse;
        pstTimerInfo->iCount = iCount;

        if (pData != NULL)
        {
            pstTimerInfo->pData = pData;
        }
    }

    return true;
}

void CTimerManager::DelTimer( HTIMER hTimer )
{
    STimerInfo* pstTimerInfo = __Handle2Info(hTimer);
    if (!pstTimerInfo)
    {
        return;
    }

    if (__TimerPending(*pstTimerInfo))
    {
        __DetachTimer(*pstTimerInfo, true);
        m_pool_timer_info.FreeObj(pstTimerInfo);
    }
    else
        pstTimerInfo->iCount = 0;
}

INT32 CTimerManager::GetCount( HTIMER hTimer )
{
    STimerInfo* pstTimerInfo = __Handle2Info(hTimer);
    if (!pstTimerInfo)
    {
        return 0;
    }

    return pstTimerInfo->iCount;
}

bool CTimerManager::Update( UINT32 dwRunTime )
{
    if (m_is_exact)
    {
        return __ExactUpdate();
    }

    return __NormalUpdate(dwRunTime);
}

void CTimerManager::Release( void )
{
	UnInit();
	delete this;
}

void* CTimerManager::GetData( HTIMER hTimer )
{
    STimerInfo* pstTimerInfo = __Handle2Info(hTimer);

    if (!pstTimerInfo)
    {
        return NULL;
    }

    return pstTimerInfo->pData;
}

void CTimerManager::__AddTimer( STimerInfo& rTimerInfo )
{
	UINT32 expires = rTimerInfo.dwExpires;

	UINT32 idx = expires - m_dwLastTick;
	struct list_head* vec;

	if (idx < TVR_SIZE)
	{
		INT32 i = expires & TVR_MASK;
		vec = m_tv1.vec + i;
	}
	else if (idx < 1 << (TVR_BITS + TVN_BITS))
	{
		INT32 i = (expires >> TVR_BITS) & TVN_MASK;
		vec = m_tv2.vec + i;
	}
	else if (idx < 1 <<(TVR_BITS + 2*TVN_BITS))
	{
		INT32 i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
		vec = m_tv3.vec + i;
	}
	else if (idx < 1 <<(TVR_BITS + 3*TVN_BITS))
	{
		INT32 i = (expires >> (TVR_BITS + 2*TVN_BITS)) & TVN_MASK;
		vec = m_tv4.vec + i;
	}
	else if ((INT32)idx < 0)
	{
		/*  
		 * Can happen if you add a timer with expires == jiffies,  
		 * or you set a timer to go off in the past  
		 */
		vec = m_tv1.vec + (m_dwLastTick & TVR_MASK);
	}
	else
	{
		/* If the timeout is larger than 0xffffffff on 64-bit  
		 * architectures then we use the maximum timeout:  
		 */
		INT32 i;
		if (idx > 0xffffffffUL)
		{
			idx = 0xffffffffUL;
			expires = idx + m_dwLastTick;
		}

		i = (expires >> (TVR_BITS + 3*TVN_BITS)) & TVN_MASK;

		vec = m_tv5.vec + i;
	}

	list_add_tail(&rTimerInfo.entry, vec);
}
#ifdef _DEBUG
#include <assert.h>
#endif

INT32 CTimerManager::__Cascade( TVec* tv, INT32 index )
{
	STimerInfo* timer;
	STimerInfo* tmp;
	struct list_head tv_list;

	list_replace_init(tv->vec + index, &tv_list);

    for (timer = __GetTimerInfoByEntry(tv_list.next), tmp = __GetTimerInfoByEntry(timer->entry.next); &timer->entry != (&tv_list);
        timer = tmp, tmp = __GetTimerInfoByEntry(tmp->entry.next))
    {
        __AddTimer(*timer);
    }

	return index;
}

bool CTimerManager::__TimerPending( STimerInfo& rTimerInfo )
{
    return rTimerInfo.entry.next != NULL;
}

void CTimerManager::__DetachTimer( STimerInfo& rTimerInfo, bool bClearPending )
{
    struct list_head *entry = &rTimerInfo.entry;

    __list_del(entry->prev, entry->next);

    if (bClearPending)
        entry->next = NULL;
    entry->prev = (struct list_head *)LIST_POISON2;
}

STimerInfo* CTimerManager::__GetTimerInfoByEntry( list_head* head )
{
    const struct list_head* ptr = head;
    return (struct timer_list*)((char*)ptr - offsetof(struct timer_list, entry));
}

bool CTimerManager::__ExactUpdate( void )
{
    bool bDeal = false;
    UINT32 dwTick = timeGetTime();

    STimerInfo* timer;

    while (dwTick != m_dwLastTick)
    {
        struct list_head work_list;
        struct list_head *head = &work_list;

        INT32 index = m_dwLastTick & TVR_MASK;

        if (!index &&
            (!__Cascade(&m_tv2, INDEX(0))) &&
            (!__Cascade(&m_tv3, INDEX(1))) &&
            !__Cascade(&m_tv4, INDEX(2)))
            __Cascade(&m_tv5, INDEX(3));

        list_replace_init(m_tv1.vec+ index, &work_list);

        while (!list_empty(head))
        {
            bDeal = true;

            timer = __GetTimerInfoByEntry(head->next);

            __DetachTimer(*timer, true);

            if (0 == timer->iCount)
            {
                m_pool_timer_info.FreeObj(timer);
            }
            else
            {
                if (timer->iCount > 0)
                {
                    --timer->iCount;
                }

                m_poTimerResponse->OnTimer(timer, timer->pData);

                if (0 == timer->iCount)
                {
                    m_pool_timer_info.FreeObj(timer);
                }
                else
                {
                    timer->dwExpires = timeGetTime() + timer->iElapse;

                    __AddTimer(*timer);
                }
            }

        }

        ++m_dwLastTick;
    }


    return bDeal;
}

bool CTimerManager::__NormalUpdate( UINT32 run_time )
{
    bool bDeal = false;
    UINT32 dwTick = timeGetTime();
    UINT32 dwCurrentTick = dwTick;
    bool bTimeOut = false;

    STimerInfo* timer;

    while (dwTick != m_dwLastTick)
    {
        struct list_head work_list;
        struct list_head *head = &work_list;

        INT32 index = m_dwLastTick & TVR_MASK;

        if (!index &&
            (!__Cascade(&m_tv2, INDEX(0))) &&
            (!__Cascade(&m_tv3, INDEX(1))) &&
            !__Cascade(&m_tv4, INDEX(2)))
            __Cascade(&m_tv5, INDEX(3));

        list_replace_init(m_tv1.vec+ index, &work_list);

        while (!list_empty(head))
        {
            bDeal = true;

            timer = __GetTimerInfoByEntry(head->next);

            __DetachTimer(*timer, true);

            if (0 == timer->iCount)
            {
                m_pool_timer_info.FreeObj(timer);
            }
            else
            {
                dwCurrentTick = timeGetTime();

                if (run_time)
                {
                    if (!bTimeOut)
                    {
                        if (dwCurrentTick - dwTick > run_time)
                        {
                            bTimeOut = true;
                        }
                    }
                }

                if (bTimeOut)
                {
                    timer->dwExpires = dwCurrentTick;

                    __AddTimer(*timer);
                }
                else
                {
                    if (timer->iCount > 0)
                    {
                        --timer->iCount;
                    }

                    m_poTimerResponse->OnTimer(timer, timer->pData);

                    if (0 == timer->iCount)
                    {
                        m_pool_timer_info.FreeObj(timer);
                    }
                    else
                    {
                        timer->dwExpires = dwCurrentTick + timer->iElapse;

                        __AddTimer(*timer);
                    }
                }
            }

        }

        ++m_dwLastTick;

        if (bTimeOut)
        {
            return bDeal;
        }
    }


    return bDeal;
}

